﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.WindowsAPICodePack.Dialogs;
using OfficeOpenXml;
using OfficeOpenXml.Style;

namespace ShieldTunnel2
{
	public partial class GeneralRiskEvalPanel : UserControl
	{
		private readonly ValueTuple<string, int>[] WeakRockOptions =
		{
			("1.Ⅴ、Ⅵ围岩长度占全隧道长度95%以上", 4),
			("2.Ⅴ、Ⅵ围岩长度占全隧道长度70%以上、95%以下", 3),
			("3.Ⅴ、Ⅵ围岩长度占全隧道长度40%以上、70%以下", 2),
			("4.Ⅴ、Ⅵ围岩长度占全隧道长度20%以上、40%以下", 1),
			("5.Ⅴ、Ⅵ围岩长度占全隧道长度20%以下", 0),
		};

		private readonly ValueTuple<string, int>[] GasContentOptions =
		{
			("1.隧道洞身穿越瓦斯地层，且瓦斯浓度为5%~16%的爆炸浓度", 3),
			("2.隧道洞身穿越瓦斯地层，且瓦斯浓度小于5%或大于16%的爆炸浓度", 2),
			("3.隧道洞身附近可能存在瓦斯地层", 1),
			("4.隧道施工区域不会出现瓦斯地层", 0),
		};

		private readonly ValueTuple<string, int>[] WaterContentOptions =
		{
			("1.隧道全程存在溶洞、地下河、涌水突泥、地质断层等地质", 3),
			("2.隧道部分区段存在溶洞溶洞、地下河、涌水突泥、地质断层等地质", 2),
			("3.隧道部分区段可能发生涌水突泥的地质", 1),
			("4.无突水涌泥可能的地质", 0),
		};

		private readonly ValueTuple<string, int>[] TunnelDiameterOptions =
		{
			("1.超特大型盾构（大于17m）", 4),
			("2.特大型盾构（大于14m、小于17m）", 3),
			("3.大型盾构（大于6m、小于14m）", 2),
			("4.中型盾构（大于3.5m、小于6m）", 1),
			("5.小型盾构（小于3.5m）", 0),
		};

		private readonly ValueTuple<string, int>[] TunnelLengthOptions =
		{
			("1.特长（3000m以上）", 4),
			("2.长（大于1000m、小于3000m）", 3),
			("3.中（大于500m、小于1000m）", 2),
			("4.短（小于500m）", 1),
		};

		private readonly ValueTuple<string, int>[] TunnelDepthOptions =
		{
			("1.超浅埋（小于6m）", 4),
			("2.浅埋（大于6m、小于12m）", 3),
			("3.中埋（大于12m、小于18m）", 2),
			("4.深埋（大于18m）", 1),
		};

		private readonly ValueTuple<string, int>[] AdjacentOptions =
		{
			("1.非常接近（小于0.3倍隧道外径）", 4),
			("2.接近（大于0.3倍、小于0.7倍隧道外径）", 3),
			("3.较接近（大于0.7倍、小于1倍隧道外径）", 2),
			("4.不接近（大于1倍隧道外径）", 1),
		};

		private EvalResult _evalResult = null;

		public GeneralRiskEvalPanel()
		{
			InitializeComponent();

			//
			ListBoxGasContent.ItemsSource = GasContentOptions.Select(o => o.Item1);
			ListBoxGasContent.SelectedIndex = GasContentOptions.Length - 1;
			ListBoxWaterContent.ItemsSource = WaterContentOptions.Select(o => o.Item1);
			ListBoxWaterContent.SelectedIndex = WaterContentOptions.Length - 1;
		}

		#region Control event handlers

		private void ButtonEvalGeneralRisk_OnClick(object sender, RoutedEventArgs e)
		{
			var tunnelLength = double.Parse(TextBoxTunnelLength.EditValue.ToString());
			var weakRockLength = double.Parse(TextBoxWeakRockLength.EditValue.ToString());
			var tunnelDiameter = double.Parse(TextBoxTunnelDiameter.EditValue.ToString());
			var tunnelDepth = double.Parse(TextBoxTunnelDepth.EditValue.ToString());

			var index = ListBoxGasContent.SelectedIndex;
			var gasContent = GasContentOptions
				.ElementAt(index)
				.Item2;
			index = ListBoxWaterContent.SelectedIndex;
			var waterContent = WaterContentOptions
				.ElementAt(index)
				.Item2;

			var adjacent = double.Parse(TextBoxAdjacent.EditValue.ToString());

			Debug.Assert(weakRockLength <= tunnelLength);
			var weakRockScores = WeakRockOptions
				.Select(o => o.Item2)
				.OrderBy(s => s)
				.ToArray();
			var a = MatchLevel(weakRockLength / tunnelLength,
				new[] {0d, 0.2d, 0.4d, 0.7d, 0.95d, 1.0d},
				weakRockScores);
			var b = gasContent;
			var c = waterContent;
			var G = a + b + c;

			var tunnelDiameterScores = TunnelDiameterOptions
				.Select(o => o.Item2)
				.OrderBy(s => s)
				.ToArray();
			var D = MatchLevel(tunnelDiameter,
				new[] {0d, 3.5d, 6d, 14d, 17d, double.MaxValue},
				tunnelDiameterScores);

			var tunnelLengthScores = TunnelLengthOptions
				.Select(o => o.Item2)
				.OrderBy(s => s)
				.ToArray();
			var L = MatchLevel(tunnelLength,
				new[] {0d, 500d, 1000d, 3000d, double.MaxValue},
				tunnelLengthScores);

			var tunnelDepthScores = TunnelDepthOptions
				.Select(o => o.Item2)
				.OrderByDescending(s => s)
				.ToArray();
			var B = MatchLevel(tunnelDepth,
				new[] {0d, 6d, 12d, 18d, double.MaxValue},
				tunnelDepthScores);

			var adjacentScores = AdjacentOptions
				.Select(o => o.Item2)
				.OrderByDescending(s => s)
				.ToArray();
			var N = MatchLevel(adjacent / tunnelDiameter,
				new[] {0d, 0.3d, 0.7d, 1.0d, double.MaxValue},
				adjacentScores);

			var R = G * (D + L + B + N);

			_evalResult = new EvalResult()
			{
				WeakRockScore = a,
				GasContentScore = b,
				WaterContentScore = c,
				TunnelDiameterScore = D,
				TunnelLengthScore = L,
				TunnelDepthScore = B,
				AdjacentScore = N,
				TotalScore = R,
			};

			var evalScoreItems = new List<EvalScoreItem>()
			{
				new EvalScoreItem(1, -1, "地质G=a+b+c", G),
				new EvalScoreItem(2, 1, "围岩情况a", a),
				new EvalScoreItem(3, 1, "瓦斯含量b", b),
				new EvalScoreItem(4, 1, "富水情况c", c),
				new EvalScoreItem(5, -1, "盾构直径D", D),
				new EvalScoreItem(6, -1, "隧道全长L", L),
				new EvalScoreItem(7, -1, "隧道埋深B", L),
				new EvalScoreItem(8, -1, "与周围环境设施临近关系N", L),
				new EvalScoreItem(9, -1, "总分R=G(D+L+B+N)", R),
			};

			Debug.Assert(TreeListGeneralRiskResult != null);
			TreeListGeneralRiskResult.ItemsSource = evalScoreItems;
			TreeListGeneralRiskResult.View.ExpandAllNodes();

			//
			// Eval general risk level
			//
			var riskLevel = MatchLevel(R,
				new[] {0, 10, 17, 25, int.MaxValue},
				new[] {1, 2, 3, 4});

			var riskLevelString = new[]
			{
				"Ⅰ级（低度风险）",
				"Ⅱ级（中度风险）",
				"Ⅲ级（高度风险）",
				"Ⅳ级（极高风险）"
			}[riskLevel - 1];
			TextBlockRiskLevel.Text = riskLevelString;
			var riskLevelColor = new[]
			{
				Colors.Green,
				Colors.Yellow,
				Colors.Orange,
				Colors.Red,
			}[riskLevel - 1];
			RectRiskLevelFill.Fill 
				= new SolidColorBrush(riskLevelColor);
			RectRiskLevelFill.Width
				= RectRiskLevelFrame.ActualWidth * riskLevel / 4.0;

			var needSpecialRiskEval = riskLevel >= 3;
			TextBlockNeedSpecialRiskEval.Text
				= needSpecialRiskEval
					? "需要专项评估"
					: "不需要专项评估";
			TextBlockNeedSpecialRiskEval.Foreground
				= new SolidColorBrush(needSpecialRiskEval
					? Colors.Red
					: Colors.Black);
		}

		private void ButtonExportEvalResult_OnClick(object sender, RoutedEventArgs e)
		{
			var ofd = new CommonSaveFileDialog()
			{
				DefaultFileName = "总体风险评估结果",
				DefaultExtension = "xlsx",
			};
			if (ofd.ShowDialog() != CommonFileDialogResult.Ok)
				return;

			var filePath = ofd.FileName;
			var fi = new FileInfo(filePath);
			if (fi.Exists)
			{
				fi.Delete();
				fi = new FileInfo(filePath);
			}

			using (var excelPackage = new ExcelPackage(fi))
			{
				var sheet = excelPackage.Workbook.Worksheets.Add("总体风险评估结果");

				//
				// 表头
				//
				var row = 1;
				var col = 1;
				sheet.Cells[row, 1].Value = "评估指标";
				sheet.Cells[row, 2].Value = "分类";
				var range = sheet.Cells[row, 2, row, 3];
				range.Merge = true;

				sheet.Cells[row, 4].Value = "评估";
				sheet.Cells[row, 5].Value = "分值";
				range = sheet.Cells[row, 1, row, 5];
				range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
				range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
				range.Style.Font.Bold = true;
				range.Style.Border.BorderAround(ExcelBorderStyle.Medium);

				sheet.Row(1).Height = 30;

				void FillMergedCellsContent(ExcelWorksheet s, 
					int fromRow, int fromCol, 
					int rowCount, int colCount, 
					string content)
				{
					s.Cells[fromRow, fromCol].Value = content;
					var r = s.Cells[fromRow, fromCol,
						fromRow + rowCount - 1, fromCol + colCount - 1];
					r.Merge = true;
					r.Style.WrapText = true;
					r.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
					r.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
					r.Style.Border.BorderAround(ExcelBorderStyle.Thin);
				}

				void FillGeoContentData(ExcelWorksheet s, int startRow, int startCol, 
					string header,
					ValueTuple<string, int>[] options, int score)
				{
					var curRow = startRow;
					var curCol = startCol;
					FillMergedCellsContent(s, curRow, curCol, options.Length, 1, header);

					ExcelRange r;
					foreach (var option in options)
					{
						s.Cells[curRow, curCol + 1].Value = option.Item1;
						if (score == option.Item2)
						{
							s.Cells[curRow, curCol + 2].Value = "√";
							s.Cells[curRow, curCol + 3].Value = score;
							r = s.Cells[curRow, curCol + 2, curRow, curCol + 3];
							r.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
						}
						curRow++;
					}

					r = s.Cells[startRow, startCol, startRow + options.Length - 1, startCol + 3];
					r.Style.Border.BorderAround(ExcelBorderStyle.Thin);
				}

				void FillContentData(ExcelWorksheet s, int startRow, int startCol,
					string header,
					ValueTuple<string, int>[] options, int score)
				{
					var curRow = startRow;
					var curCol = startCol;
					FillMergedCellsContent(s, curRow, curCol, options.Length, 1, header);

					ExcelRange r;
					foreach (var option in options)
					{
						s.Cells[curRow, curCol + 1].Value = option.Item1;
						r = s.Cells[curRow, curCol + 1, curRow, curCol + 2];
						r.Merge = true;
						if (score == option.Item2)
						{
							s.Cells[curRow, curCol + 3].Value = "√";
							s.Cells[curRow, curCol + 4].Value = score;
							r = s.Cells[curRow, curCol + 3, curRow, curCol + 4];
							r.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
						}
						curRow++;
					}

					r = s.Cells[startRow, startCol, startRow + options.Length - 1, startCol + 4];
					r.Style.Border.BorderAround(ExcelBorderStyle.Thin);
				}

				//
				// 地质
				//
				row = 2;
				col = 1;
				FillMergedCellsContent(sheet, row, col, 13, 1, "地质\nG=a+b+c");

				col = 2;
				FillGeoContentData(sheet, row, col, "围岩情况\na",
					WeakRockOptions, _evalResult.WeakRockScore);
				row += WeakRockOptions.Length;

				FillGeoContentData(sheet, row, col, "瓦斯含量\nb",
					GasContentOptions, _evalResult.GasContentScore);
				row += GasContentOptions.Length;

				FillGeoContentData(sheet, row, col, "富水情况\nb",
					WaterContentOptions, _evalResult.WaterContentScore);
				row += WaterContentOptions.Length;

				//
				// 盾构直径
				//
				col = 1;
				FillContentData(sheet, row, col, "盾构直径\nD",
					TunnelDiameterOptions, _evalResult.TunnelDiameterScore);
				row += TunnelDiameterOptions.Length;

				//
				// 隧道全长
				//
				FillContentData(sheet, row, col, "隧道全长\nL",
					TunnelLengthOptions, _evalResult.TunnelLengthScore);
				row += TunnelLengthOptions.Length;

				//
				// 隧道埋深
				//
				FillContentData(sheet, row, col, "隧道埋深\nB",
					TunnelDepthOptions, _evalResult.TunnelDepthScore);
				row += TunnelDepthOptions.Length;

				//
				// 与周围环境设施临近关系
				//
				FillContentData(sheet, row, col, "与周围环境设施临近关系\nN",
					AdjacentOptions, _evalResult.AdjacentScore);
				row += AdjacentOptions.Length;

				//
				// 总分
				//
				sheet.Cells[row, 1].Value = "总分\nR";
				sheet.Cells[row, 2].Value = "R=G(D+L+B+N)";
				sheet.Cells[row, 5].Value = _evalResult.TotalScore;
				range = sheet.Cells[row, 2, row, 4];
				range.Merge = true;

				range = sheet.Cells[row, 1];
				range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
				range = sheet.Cells[row, 5];
				range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
				range = sheet.Cells[row, 1, row, 5];
				range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;

				sheet.Row(row).Height = 25;

				range = sheet.Cells[1, 1, row, 5];
				range.Style.Border.BorderAround(ExcelBorderStyle.Medium);

				//
				excelPackage.SaveAs(fi);
			}
		}

		#endregion

		#region Helper methods

		private static TGrade MatchLevel<TScore, TGrade>(TScore score,
			IList<TScore> scores, IList<TGrade> grades)
			where TScore : IComparable
		{
			Debug.Assert(scores != null && grades != null
			             && grades.Count >= 1
			             && scores.Count == grades.Count + 1);
			Debug.Assert(score.CompareTo(scores.First()) >= 0
			             && score.CompareTo(scores.Last()) <= 0);

			var grade = grades.Last();
			for (var i = 0; i < grades.Count; i++)
			{
				var lower = scores[i];
				var upper = scores[i + 1];
				Debug.Assert(lower.CompareTo(upper) < 0);
				if (score.CompareTo(lower) >= 0 && score.CompareTo(upper) < 0)
				{
					grade = grades[i];
					break;
				}
			}
			return grade;
		}

		#endregion

		#region Helper Classes

		public class EvalScoreItem
		{
			public EvalScoreItem(int id, int parentId, string scoreName, int score)
			{
				Id = id;
				ParentId = parentId;
				ScoreName = scoreName;
				Score = score;
			}

			public int Id { get; set; }
			public int ParentId { get; set; }
			public string ScoreName { get; set; }
			public int Score { get; set; }
		}

		public class EvalResult
		{
			public int WeakRockScore { get; set; }
			public int GasContentScore { get; set; }
			public int WaterContentScore { get; set; }
			public int TunnelDiameterScore { get; set; }
			public int TunnelLengthScore { get; set; }
			public int TunnelDepthScore { get; set; }
			public int AdjacentScore { get; set; }

			public int TotalScore { get; set; }
		}

		#endregion
	}
}
